package com.dd.stack.es.service.task.data.gene.ecommerce;

import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import net.datafaker.Faker;

import java.io.IOException;
import java.sql.Timestamp;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author liuxianmeng
 * @CreateTime 2025/4/18 6:09
 * @Description 电商复杂数据插入ElasticSearch 模拟测试数据
 *
 *  -> 创建包含复杂数据结构的索引，并批量插入100万条模拟电商商品数据
 */
public class EcommerceDataGenerator {

    /**
     * 主入口方法
     */
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();

        try (RestHighLevelClient client = createElasticsearchClient()) {
            // 1. 创建索引（如果不存在）
            //createIndexIfNotExists(client);

            // 2. 执行批量数据插入
            bulkInsertData(client);

        } catch (Exception e) {
            e.printStackTrace();
        }

        // 计算并打印总耗时
        long duration = System.currentTimeMillis() - startTime;
        System.out.printf("\n执行完成! 总耗时: %d分 %d秒%n",
                TimeUnit.MILLISECONDS.toMinutes(duration),
                TimeUnit.MILLISECONDS.toSeconds(duration) -
                        TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)));
    }

    // 索引名称常量
    private static final String INDEX_NAME = "ecommerce_products_java";

    // 数据量配置常量
    private static final long TOTAL_DOCUMENTS = 10000L; // 总文档数
    private static final int BATCH_SIZE = 2000;          // 每批次插入量

    // 数据生成预定义选项
    private static final List<String> CATEGORIES = Arrays.asList(
            "Electronics", "Clothing", "Home", "Books", "Sports", "Beauty", "Toys", "Food"
    );

    private static final List<String> TAGS = Arrays.asList(
            "new", "sale", "popular", "limited", "eco-friendly", "premium", "bestseller"
    );

    private static final List<String> SPEC_GROUPS = Arrays.asList(
            "Technical", "General", "Material", "Care"
    );

    private static final List<String> COLORS = Arrays.asList(
            "Red", "Blue", "Green", "Black", "White", "Silver"
    );

    private static final List<String> SIZES = Arrays.asList(
            "S", "M", "L", "XL", "XXL"
    );

    /**
     * 创建Elasticsearch客户端实例
     */
    private static RestHighLevelClient createElasticsearchClient() {
        // 配置连接本地9200端口的Elasticsearch
        return new RestHighLevelClient(
                RestClient.builder(new HttpHost("43.136.170.102", 9200, "http")));
    }

    /**
     * 创建索引（如果不存在）
     * @param client Elasticsearch客户端实例
     */
    private static void createIndexIfNotExists(RestHighLevelClient client) throws IOException {
        // 检查索引是否已存在
        if (!client.indices().exists(new GetIndexRequest(INDEX_NAME), RequestOptions.DEFAULT)) {
            // 索引映射定义（JSON格式）
            String mapping = "{\n" +
                    "  \"settings\": {\n" +
                    "    \"number_of_shards\": 1,\n" +          // 单分片
                    "    \"number_of_replicas\": 0,\n" +        // 无副本
                    "    \"refresh_interval\": \"30s\"\n" +     // 刷新间隔
                    "  },\n" +
                    "  \"mappings\": {\n" +
                    "    \"properties\": {\n" +
                    "      \"product_id\": {\"type\": \"keyword\"},\n" +
                    "      \"name\": {\n" +
                    "        \"type\": \"text\",\n" +
                    "        \"fields\": {\n" +
                    "          \"keyword\": {\"type\": \"keyword\"}\n" +
                    "        }\n" +
                    "      },\n" +
                    "      \"description\": {\"type\": \"text\"},\n" +
                    "      \"price\": {\"type\": \"double\"},\n" +
                    "      \"cost\": {\"type\": \"double\"},\n" +
                    "      \"discount\": {\"type\": \"float\"},\n" +
                    "      \"stock\": {\"type\": \"integer\"},\n" +
                    "      \"is_active\": {\"type\": \"boolean\"},\n" +
                    "      \"created_at\": {\"type\": \"date\"},\n" +
                    "      \"updated_at\": {\"type\": \"date\"},\n" +
                    "      \"categories\": {\"type\": \"keyword\"},\n" +
                    "      \"tags\": {\"type\": \"keyword\"},\n" +
                    "      \"attributes\": {\n" +
                    "        \"type\": \"nested\",\n" +
                    "        \"properties\": {\n" +
                    "          \"name\": {\"type\": \"keyword\"},\n" +
                    "          \"value\": {\"type\": \"text\"},\n" +
                    "          \"is_visible\": {\"type\": \"boolean\"}\n" +
                    "        }\n" +
                    "      },\n" +
                    "      \"specifications\": {\n" +
                    "        \"type\": \"nested\",\n" +
                    "        \"properties\": {\n" +
                    "          \"group\": {\"type\": \"keyword\"},\n" +
                    "          \"items\": {\n" +
                    "            \"type\": \"nested\",\n" +
                    "            \"properties\": {\n" +
                    "              \"key\": {\"type\": \"keyword\"},\n" +
                    "              \"value\": {\"type\": \"text\"}\n" +
                    "            }\n" +
                    "          }\n" +
                    "        }\n" +
                    "      },\n" +
                    "      \"reviews\": {\n" +
                    "        \"type\": \"nested\",\n" +
                    "        \"properties\": {\n" +
                    "          \"user_id\": {\"type\": \"keyword\"},\n" +
                    "          \"username\": {\"type\": \"keyword\"},\n" +
                    "          \"rating\": {\"type\": \"integer\"},\n" +
                    "          \"comment\": {\"type\": \"text\"},\n" +
                    "          \"created_at\": {\"type\": \"date\"},\n" +
                    "          \"is_verified\": {\"type\": \"boolean\"},\n" +
                    "          \"helpful_count\": {\"type\": \"integer\"}\n" +
                    "        }\n" +
                    "      },\n" +
                    "      \"sales_data\": {\n" +
                    "        \"properties\": {\n" +
                    "          \"last_30_days\": {\"type\": \"integer\"},\n" +
                    "          \"total\": {\"type\": \"integer\"},\n" +
                    "          \"return_rate\": {\"type\": \"float\"}\n" +
                    "        }\n" +
                    "      },\n" +
                    "      \"location\": {\"type\": \"geo_point\"},\n" +
                    "      \"weight\": {\"type\": \"float\"},\n" +
                    "      \"dimensions\": {\n" +
                    "        \"properties\": {\n" +
                    "          \"length\": {\"type\": \"float\"},\n" +
                    "          \"width\": {\"type\": \"float\"},\n" +
                    "          \"height\": {\"type\": \"float\"},\n" +
                    "          \"unit\": {\"type\": \"keyword\"}\n" +
                    "        }\n" +
                    "      },\n" +
                    "      \"manufacturer\": {\n" +
                    "        \"properties\": {\n" +
                    "          \"name\": {\"type\": \"keyword\"},\n" +
                    "          \"country\": {\"type\": \"keyword\"},\n" +
                    "          \"founded_year\": {\"type\": \"integer\"}\n" +
                    "        }\n" +
                    "      },\n" +
                    "      \"variants\": {\n" +
                    "        \"type\": \"nested\",\n" +
                    "        \"properties\": {\n" +
                    "          \"sku\": {\"type\": \"keyword\"},\n" +
                    "          \"color\": {\"type\": \"keyword\"},\n" +
                    "          \"size\": {\"type\": \"keyword\"},\n" +
                    "          \"price_diff\": {\"type\": \"float\"},\n" +
                    "          \"stock\": {\"type\": \"integer\"}\n" +
                    "        }\n" +
                    "      }\n" +
                    "    }\n" +
                    "  }\n" +
                    "}";

            // 创建索引请求
            CreateIndexRequest request = new CreateIndexRequest(INDEX_NAME);
            request.source(mapping, XContentType.JSON);
            client.indices().create(request, RequestOptions.DEFAULT);
            System.out.println("索引创建成功");
        } else {
            System.out.println("索引已存在，跳过创建");
        }
    }

    /**
     * 批量插入数据到Elasticsearch
     * @param client Elasticsearch客户端实例
     */
    private static void bulkInsertData(RestHighLevelClient client) throws IOException {
        Faker faker = new Faker(new Locale("en-US")); // 初始化数据生成器
        Random random = new Random();                  // 随机数生成器
        BulkRequest bulkRequest = new BulkRequest();   // 批量请求对象

        System.out.printf("开始插入 %,d 条文档...%n", TOTAL_DOCUMENTS);

        for (int i = 1; i <= TOTAL_DOCUMENTS; i++) {
            // 1. 生成单个商品文档
            Map<String, Object> product = generateProduct(faker, random, i);

            // 2. 添加到批量请求
            bulkRequest.add(new IndexRequest(INDEX_NAME).source(product));

            // 3. 批量提交（达到批次大小时）
            if (i % BATCH_SIZE == 0 || i == TOTAL_DOCUMENTS) {
                BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);

                // 处理失败情况
                if (response.hasFailures()) {
                    System.err.println("批量插入存在失败: " + response.buildFailureMessage());
                }

                // 重置批量请求对象
                bulkRequest = new BulkRequest();
                System.out.printf("已处理: %,d / %,d (%.1f%%)%n",
                        i, TOTAL_DOCUMENTS, (i * 100.0 / TOTAL_DOCUMENTS));
            }
        }
    }

    /**
     * 生成单个商品文档数据
     * @param faker 数据生成器
     * @param random 随机数生成器
     * @param id 商品ID序号
     * @return 包含商品数据的Map
     */
    private static Map<String, Object> generateProduct(Faker faker, Random random, int id) {
        Map<String, Object> product = new LinkedHashMap<>(); // 保持字段顺序

        // 1. 生成时间信息
        // 当前时间
        ZonedDateTime now = ZonedDateTime.now();
        // 生成2年前的随机时间（转为Timestamp）
        Timestamp pastTimestamp = new Timestamp(
                faker.date().past(365 * 2, TimeUnit.DAYS, Date.from(now.toInstant())).getTime()
        );
        ZonedDateTime createdAtUnformatted = pastTimestamp.toInstant().atZone(now.getZone());
        // 生成createdAt和now之间的随机时间（使用Timestamp参数）
        Timestamp nowTimestamp = new Timestamp(Date.from(now.toInstant()).getTime());
        Timestamp updatedTimestamp = faker.date().between(
                new Timestamp(Date.from(createdAtUnformatted.toInstant()).getTime()), // 明确转为Timestamp
                nowTimestamp
        );
        ZonedDateTime updatedAtUnformatted = updatedTimestamp.toInstant().atZone(now.getZone());
        // put日期数据前进行格式化 使用ISO格式，不带时区括号
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
        String createdAt = createdAtUnformatted.format(formatter);
        String updatedAt = updatedAtUnformatted.format(formatter);

        // 2. 基础商品信息
        product.put("product_id", String.format("prod_%07d", id));
        product.put("name", faker.commerce().productName());
        product.put("description", String.join("\n", faker.lorem().paragraphs(3)));
        product.put("price", Double.parseDouble(faker.commerce().price(10, 1000)));
        product.put("cost", Double.parseDouble(faker.commerce().price(5, 500)));
        product.put("discount", random.nextFloat() * 0.5f); // 0-50%折扣
        product.put("stock", random.nextInt(1001));        // 库存0-1000
        product.put("is_active", random.nextBoolean());    // 是否上架
        product.put("created_at", createdAt);   // 创建时间
        product.put("updated_at", updatedAt);   // 更新时间

        // 3. 分类和标签（随机选择）
        product.put("categories", getRandomSubset(CATEGORIES, 1, 3, random));
        product.put("tags", getRandomSubset(TAGS, 0, 4, random));

        // 4. 商品属性（嵌套对象）
        product.put("attributes", generateAttributes(faker, random));

        // 5. 商品规格（多级嵌套）
        product.put("specifications", generateSpecifications(faker, random));

        // 6. 商品评论（嵌套数组）
        product.put("reviews", generateReviews(faker, random, createdAtUnformatted, now));

        // 7. 销售数据
        Map<String, Object> salesData = new HashMap<>();
        salesData.put("last_30_days", random.nextInt(501));     // 最近30天销量
        salesData.put("total", random.nextInt(10001));          // 总销量
        salesData.put("return_rate", random.nextFloat() * 0.15f); // 退货率0-15%
        product.put("sales_data", salesData);

        // 8. 地理位置
        product.put("location", String.format("%f,%f",
        Double.parseDouble(faker.address().latitude()),
        Double.parseDouble(faker.address().longitude())));

        // 9. 重量和尺寸
        product.put("weight", 0.1 + random.nextFloat() * 20); // 0.1-20kg
        Map<String, Object> dimensions = new HashMap<>();
        dimensions.put("length", 5 + random.nextFloat() * 95);  // 5-100cm/inch
        dimensions.put("width", 5 + random.nextFloat() * 45);   // 5-50cm/inch
        dimensions.put("height", 1 + random.nextFloat() * 29);  // 1-30cm/inch
        dimensions.put("unit", random.nextBoolean() ? "cm" : "inch");
        product.put("dimensions", dimensions);

        // 10. 制造商信息
        Map<String, Object> manufacturer = new HashMap<>();
        manufacturer.put("name", faker.company().name());
        manufacturer.put("country", faker.address().country());
        manufacturer.put("founded_year", 1950 + random.nextInt(71)); // 1950-2020
        product.put("manufacturer", manufacturer);

        // 11. 商品变体（颜色、尺寸等）
        product.put("variants", generateVariants(random, product.get("product_id").toString()));

        return product;
    }

    /**
     * 生成商品属性列表
     */
    private static List<Map<String, Object>> generateAttributes(Faker faker, Random random) {
        List<Map<String, Object>> attributes = new ArrayList<>();
        int count = 2 + random.nextInt(5); // 生成2-6个属性

        for (int i = 0; i < count; i++) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", faker.lorem().word()); // 属性名
            map.put("value", faker.lorem().sentence()); // 属性值
            map.put("is_visible", random.nextBoolean()); // 是否可见
            attributes.add(map);
        }
        return attributes;
    }

    /**
     * 生成商品规格（多级嵌套结构）
     */
    private static List<Map<String, Object>> generateSpecifications(Faker faker, Random random) {
        List<Map<String, Object>> specs = new ArrayList<>();
        // 随机选择1-3个规格组
        List<String> groups = getRandomSubset(SPEC_GROUPS, 1, 3, random);

        for (String group : groups) {
            List<Map<String, Object>> items = new ArrayList<>();
            int itemCount = 2 + random.nextInt(4); // 每组2-5个条目

            for (int i = 0; i < itemCount; i++) {
                Map<String, Object> item = new HashMap<>();
                item.put("key", faker.lorem().word());     // 规格键
                item.put("value", faker.lorem().sentence()); // 规格值
                items.add(item);
            }

            Map<String, Object> spec = new HashMap<>();
            spec.put("group", group);  // 规格组名
            spec.put("items", items);   // 规格条目列表
            specs.add(spec);
        }
        return specs;
    }

    /**
     * 生成商品评论数据（使用Timestamp的Java 8兼容版）
     */
    private static List<Map<String, Object>> generateReviews(Faker faker, Random random,
                                                             ZonedDateTime productCreatedAt, ZonedDateTime now) {
        List<Map<String, Object>> reviews = new ArrayList<>();
        int reviewCount = random.nextInt(16); // 0-15条评论

        // 转换为Timestamp（避免重复转换）
        Timestamp productTimestamp = new Timestamp(Date.from(productCreatedAt.toInstant()).getTime());
        Timestamp nowTimestamp = new Timestamp(Date.from(now.toInstant()).getTime());

        for (int i = 0; i < reviewCount; i++) {
            // 使用Timestamp参数生成随机时间
            Timestamp reviewTimestamp = faker.date().between(
                    productTimestamp,
                    nowTimestamp
            );
            ZonedDateTime reviewTimeUnformatted = reviewTimestamp.toInstant().atZone(now.getZone());

            // 构建评论数据（Java 8兼容写法）
            Map<String, Object> review = new HashMap<>();
            review.put("user_id", "user_" + (1000 + random.nextInt(9000)));
            review.put("username", faker.name().username());
            review.put("rating", 1 + random.nextInt(5)); // 1-5星
            review.put("comment", String.join("\n", faker.lorem().paragraphs(1 + random.nextInt(3))));
            // put日期数据前进行格式化 使用ISO格式，不带时区括号
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
            String reviewTime = formatter.format(reviewTimeUnformatted);
            review.put("created_at", reviewTime);
            review.put("is_verified", random.nextBoolean());
            review.put("helpful_count", random.nextInt(51));

            reviews.add(review);
        }
        return reviews;
    }

    /**
     * 生成商品变体数据（如不同颜色、尺寸）
     */
    private static List<Map<String, Object>> generateVariants(Random random, String productId) {
        List<Map<String, Object>> variants = new ArrayList<>();
        // 随机选择1-3种颜色和1-3种尺寸
        List<String> colors = getRandomSubset(COLORS, 1, 3, random);
        List<String> sizes = getRandomSubset(SIZES, 1, 3, random);

        // 生成所有颜色和尺寸组合
        for (String color : colors) {
            for (String size : sizes) {
                HashMap<String, Object> map = new HashMap<>();
                map.put("sku", String.format("%s_%s_%s", productId, color.charAt(0), size));
                map.put("color", color);
                map.put("size", size);
                map.put("price_diff", (random.nextFloat() * 20) - 10); // -10到+10的价格差异
                map.put("stock", random.nextInt(101)); // 变体库存
                variants.add(map);
            }
        }
        return variants;
    }

    /**
     * 从数组中随机获取子集
     * @param list 源列表
     * @param min 最小选择数量
     * @param max 最大选择数量
     * @param random 随机数生成器
     * @return 随机子集列表
     */
    private static <T> List<T> getRandomSubset(List<T> list, int min, int max, Random random) {
        int count = min + random.nextInt(max - min + 1);    // 计算随机数量
        Collections.shuffle(list, random);                          // 打乱列表顺序
        return new ArrayList<>(list.subList(0, count));             // 返回前N个元素
    }
}
